

import { DownOutlined } from '@ant-design/icons';
import { Button, Dropdown, Menu, Tabs } from 'antd';
import classnames from 'classnames/bind';
import { useEffect, useState } from 'react';
import KeepAlive, { AliveScope, useAliveController } from 'react-activation';
import { connect, history } from 'umi';

const { TabPane } = Tabs;
const cx = classnames.bind();

const PageTabsLayout = (props) => {
  const {
    location: { pathname, search, query }, route, children, dispatch,
    tabInfo: { tabList, activeKey, allowedData },
  } = props;
  const { dropScope, refresh } = useAliveController(); // 清除缓存函数
  const [first, setFirst] = useState(true); // 是否首次加载

  /**
   * 修改页签数据
   */
  const changeTabList = (data) => dispatch({ type: 'tab/_setTabList', data });
  /**
   * 修改选中页签key
   */
  const changeActiveKey = (key) => dispatch({ type: 'tab/_setActiveKey', key });
  /**
   * 修改可激活页签的路由数据
   */
  const changeAlloweData = (data) => dispatch({ type: 'tab/_setAllowedData', data });

  /**
   * 根据路由名称获取该路由是否为可激活页签
   */
  const getAllowed = (pathname) => allowedData.find(item => item.path == pathname);

  /**
   * 根据key获取tab数据
   */
  const getTab = (key) => tabList.find(item => item.key == key);

  /**
   * 根据key清除页签的页面缓存
   * @param {String} value keys的字符串，传递多个可用逗号分割
   */
  const dropScopePage = (value) => value.split(',').forEach(item => dropScope(item));

  /**
   * 根据key获取tab在数组中的索引
   */
  const getTabIndex = (key) => {
    const { tabInfo: { tabList } } = props;
    return tabList.findIndex(item => item.key === key);
  }

  /**
   * 根据数组中的索引修改或删除对应的tab
   * @param {*} index 数组索引
   * @param {*} newData 为空时即为删除，否则为修改
   */
   const changeTabByIndex = (index, newData = undefined) => {
    const { tabInfo: { tabList } } = props;
    const newTabList = [...tabList];
    if (!!newData) {
      // 有新数据，修改
      newTabList.splice(index, 1, newData);
    } else {
      // 没有新数据，删除
      newTabList.splice(index, 1);
    }
    changeTabList(newTabList);
  }

  /**
   * 获取当前链接拼接的key名，如果有附带参数，key = key + search
   */
  const getTabKeyByRoute = () => {
    const isOnlyPage = getIsOnlyPage(pathname);
    if (isOnlyPage) {
      // 标识该页面只能打开一次页签，不允许多开，所以使用路由名称做为key值
      return pathname;
    }
    let searchKey = search;
    if (searchKey) {
      // 在编辑页面有传入ID后，浏览器的刷新会导致seach属性的 '?' 丢失，所以加上判断，没有则加上
      if (searchKey.indexOf('?') != 0) {
        searchKey = `?${searchKey}`;
      }
      return pathname + searchKey;
    }
    return pathname;
  };

  /**
   * 获取当前路由是否只允许打开一个页签
   */
  const getIsOnlyPage = (pathname) => !!getAllowed(pathname)?.meta?.isOnlyPage;

  /**
   *  设置当前页签的标题
   */
   const setTabTitle = (title) => {
    const { tabInfo: { tabList } } = props;
    const newTabList = [...tabList];
    const tabIndex = getTabIndex(activeKey);
    if (tabIndex > -1) {
      const newTab = { ...newTabList[tabIndex], name: title };
      changeTabByIndex(tabIndex, newTab);
    }
  }

  /**
   * @param {*} key 页签key
   */
   const onClosePage = (key) => {
    if (!!key) return close(key);
    const routeKey = getTabKeyByRoute();
    close(routeKey);
  }

  const routeKey = getTabKeyByRoute(); // 当前路由跟参数拼接出来的key

  // 首次运行 - 只加载一次
  useEffect(() => {
    const { tabList: newTabList, allowedData: newAllowedData } = handleRouteData(route.routes);
    let nowRouteKey = routeKey;
    const tab = newTabList.find(item => item.key == nowRouteKey);
    const allowed = newAllowedData.find(item => item.path == pathname);
    if (!tab && !!allowed) {
      // 只允许打开一个页签
      if (allowed.meta.isOnlyPage) {
        nowRouteKey = pathname;
      }
      // 首次加载 且是 可激活页签
      newTabList.push({
        ...allowed,
        key: nowRouteKey,
        query: query,
      });
    }
    changeTabList(newTabList); // 已激活的页签
    changeAlloweData(newAllowedData); // 可以激活页签的路由
    changeActiveKey(nowRouteKey); // 激活的页签key
    setFirst(false); // 设置是否首次加载为false
  }, []);

  const allowed = getAllowed(pathname); // 需要缓存路由的数据
  const routeWhen = !!allowed; // 当前路由是否需要缓存、激活页签

  // 点击页签变化
  const changeTabByKey = (key) => {
    if (key == routeKey) return;
    const tab = getTab(key);
    changeActiveKey(key);
    history.replace({
      pathname: tab.path,
      query: tab.query,
    });
  }

  // 页签关闭事件 - 关闭open
  const removeTabey = (activeKey, action) => {
    if (action == 'remove') {
      close(activeKey);
    }
  };

  // 关闭页签
  const close = (key) => {
    const { tabInfo: { tabList } } = props;
    const newTabList = [...tabList];
    const tabIndex = getTabIndex(key);
    if (tabIndex > -1) {
      changeTabByIndex(tabIndex);
      const isActive = props?.tabInfo?.activeKey === key; // 是否被激活的
      if (isActive) { // 被删除的是当前激活的页签
        const beforeIndex = tabIndex - 1 < 0 ? 0 : tabIndex - 1; // 取的被删页签的前一条数据索引
        const tab = newTabList[beforeIndex]; // 获取数据
        if (tab) changeTabByKey(tab.key); // 改变激活key
      }
      // removePage(key); // 删除该页签缓存的事件函数
      dropScopePage(key); // 清除缓存
    }
  }

  // 菜单点击
  const menuItemClick = (key) => {
    if (key === 'closeOther') {
      // 清除其它选项卡
      closeOther(routeKey);
    }
    if (key === 'closeAll') {
      // 清除全部选项卡
      closeAll();
    }
  };

  // 删除除了key以外所有可关闭的页签
  const closeOther = (key) => {
    const { tabInfo: { tabList } } = props;
    const arr = [...tabList];
    const newTabList = arr.filter(item => item.key === key || item.meta.closable === false);
    const delKeys = arr.filter(item => item.key !== key && item.meta.closable !== false).map(item => item.key).join();
    // removePage(delKeys); // 删除该页签缓存的事件函数
    dropScopePage(delKeys); // 清除页面缓存
    changeTabList(newTabList); // 改变数据
  };

  // 删除所有可关闭的页签
  const closeAll = () => {
    const { tabInfo: { tabList } } = props;
    const arr = [...tabList];
    const newTabList = arr.filter(item => item.meta.closable === false);
    const delKeys = arr.filter(item => item.meta.closable !== false).map(item => item.key).join();
    if (newTabList.length > 0) {
      const tab = newTabList[newTabList.length - 1];
      changeTabByKey(tab.key);
    } else {
      history.replace({
        pathname: '/',
      });
    }
    // removePage(delKeys); // 删除该页签缓存的事件函数
    dropScopePage(delKeys); // 清除页面缓存
    changeTabList(newTabList); // 改变数据
  }

  // 额外内容渲染
  const extraRender = () => {
    const items = [
      { label: '清除其它选项卡', key: 'closeOther' },
      { label: '清除全部选项卡', key: 'closeAll' },
    ];
    const menu = <Menu items={items} onClick={(e) => menuItemClick(e.key)} />;
    return (
      <div className='tabs_extra'>
        <Dropdown overlay={menu} overlayStyle={{ minWidth: 150 }}>
          <Button className="tabs_extra_btn">
            关闭操作 <DownOutlined />
          </Button>
        </Dropdown>
      </div>
    );
  };

  // 监听路由变化，使用routeKey的原因是可能存在同个详情页，但是参数不同
  useEffect(() => {
    if (first) return;
    const { tabInfo: { tabList, activeKey } } = props;
    const isOnlyPage = getIsOnlyPage(pathname);
    const tab = getTab(routeKey);
    const newTabList = [...tabList];
    if (activeKey !== routeKey) {
      if (tab && isOnlyPage && routeWhen) { // 页签存有该数据，但标记只能打开一个页签，替换掉旧的页签
        refresh(routeKey); // 刷新该路由缓存
        const tabIndex = getTabIndex(routeKey);
        newTabList.splice(tabIndex, 1, { ...tab, query });
        history.replace({
          pathname: tab.path,
          query,
        });
      } else if (tab) { // 页签存有该数据
        history.replace({
          pathname: tab.path,
          query: tab.query,
        });
      } else {// 页签没有该数据
        if (routeWhen) { // 是可激活页签
          newTabList.push({
            ...allowed,
            key: routeKey,
            query: query,
          });
        }
      }
      changeTabList(newTabList);
      changeActiveKey(routeKey);
    }
  }, [routeKey]);

  /**
   * 获取tab组件需要的数据渲染标签栏
   * @return {Array} 返回结果
   */
  const getTabItems = () => {
    return tabList.map(item => {
      const newItem = { ...item, label: item.name, closable: item.meta?.closable };
      return newItem
    });
  }

  return (
    <div className={cx('page-tabs-layout')}>
      <div className={cx('page-tabs-layout-body')}>
        <Tabs
          className={`page-tabs-layout-body-tabs`}
          type="editable-card"
          hideAdd
          activeKey={activeKey}
          onEdit={removeTabey}
          onChange={changeTabByKey}
          tabBarExtraContent={extraRender()}
          tabBarGutter={0}
          items={getTabItems()}
        />
      </div>
      <div className={cx('page-tabs-layout-content')} >
        <AliveScope>
          <KeepAlive when={routeWhen} name={routeKey} id={routeKey} saveScrollPosition="screen" >
            {children}
          </KeepAlive>
        </AliveScope>
      </div>
    </div>
  );
}

export default connect(({ tab }) => ({
  tabInfo: tab,
}))(PageTabsLayout);


// 路由处理，将数据处理成列表，提供给tab页签
const handleRouteData = (children) => {
  const [tabList, allowedData] = [[], []];
  const formatData = (data) => {
    data.forEach((item) => {
      const meta = item.meta;
      if (meta) {
        // 展示在最左方，且不可关闭
        if (meta.keepAlive && meta.closable == false) {
          tabList.push({
            ...item,
            key: item.path,
          });
        }
        // 该路由标记需要页签
        if (meta.keepAlive) allowedData.push(item);
      }
      // 该路由存在子路由
      if (item.routes) formatData(item.routes);
    });
  };
  formatData(children);
  return { tabList, allowedData };
};

